<!DOCTYPE html><html class="theme-next pisces" lang="zh-Hans"><head><meta name="generator" content="Hexo 3.9.0"><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta name="theme-color" content="#222"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css"><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4"><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4"><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4"><link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222"><meta name="keywords" content="Bitmap,"><meta name="description" content="文章转自 问题在讲解图片占用内存前，我们先问自己几个问题：  我们在对手机进行屏幕适时，常想可不可以只切一套图适配所有的手机呢？ 一张图片加载到手机中，占用内存到底有多少？ 图片占用内存跟哪些东西有关？跟手机有关系么？同一张图片放在不同的dpi文件夹下内存占用会变化么？ 如果是网络图片，加载到手机中，占用内存跟手机屏幕有关系么？  带着这些问题我们来一层层解析。我们先看看加载本地资源，不同手机所占"><meta name="keywords" content="Bitmap"><meta property="og:type" content="article"><meta property="og:title" content="Bitmap占用内存大小及加载解析"><meta property="og:url" content="https://aicode.vip/2017/07/10/Bitmap占用内存大小及加载解析/index.html"><meta property="og:site_name" content="AI CODE"><meta property="og:description" content="文章转自 问题在讲解图片占用内存前，我们先问自己几个问题：  我们在对手机进行屏幕适时，常想可不可以只切一套图适配所有的手机呢？ 一张图片加载到手机中，占用内存到底有多少？ 图片占用内存跟哪些东西有关？跟手机有关系么？同一张图片放在不同的dpi文件夹下内存占用会变化么？ 如果是网络图片，加载到手机中，占用内存跟手机屏幕有关系么？  带着这些问题我们来一层层解析。我们先看看加载本地资源，不同手机所占"><meta property="og:locale" content="zh-Hans"><meta property="og:image" content="https://upload-images.jianshu.io/upload_images/3846387-c52069b0d2e0b6be.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"><meta property="og:updated_time" content="2020-12-24T02:28:48.267Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="Bitmap占用内存大小及加载解析"><meta name="twitter:description" content="文章转自 问题在讲解图片占用内存前，我们先问自己几个问题：  我们在对手机进行屏幕适时，常想可不可以只切一套图适配所有的手机呢？ 一张图片加载到手机中，占用内存到底有多少？ 图片占用内存跟哪些东西有关？跟手机有关系么？同一张图片放在不同的dpi文件夹下内存占用会变化么？ 如果是网络图片，加载到手机中，占用内存跟手机屏幕有关系么？  带着这些问题我们来一层层解析。我们先看看加载本地资源，不同手机所占"><meta name="twitter:image" content="https://upload-images.jianshu.io/upload_images/3846387-c52069b0d2e0b6be.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",scheme:"Pisces",version:"5.1.4",sidebar:{position:"right",display:"post",offset:12,b2t:!1,scrollpercent:!0,onmobile:!1},fancybox:!0,tabs:!0,motion:{enable:!1,async:!1,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}},duoshuo:{userId:"0",author:"博主"},algolia:{applicationID:"",apiKey:"",indexName:"",hits:{per_page:10},labels:{input_placeholder:"Search for Posts",hits_empty:"We didn't find any results for the search: ${query}",hits_stats:"${hits} results found in ${time} ms"}}}</script><title>Bitmap占用内存大小及加载解析 | AI CODE</title><script type="text/javascript">var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?43ad219dbfb31aa8b5f219ce62e354d1";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script></head><body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans"><div class="container sidebar-position-right page-post-detail"><div class="headband"></div><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-wrapper"><div class="site-meta"><div class="custom-logo-site-title"><a href="/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title">AI CODE</span><span class="logo-line-after"><i></i></span></a></div><p class="site-subtitle"></p></div><div class="site-nav-toggle"> <button><span class="btn-bar"></span><span class="btn-bar"></span><span class="btn-bar"></span></button></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i><br> 首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i><br> 标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i><br> 分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br> 归档</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br> 搜索</a></li></ul><div class="site-search"><div class="popup search-popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i></span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"> <input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><div class="post-block"><link itemprop="mainEntityOfPage" href="https://aicode.vip/2017/07/10/Bitmap占用内存大小及加载解析/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content><meta itemprop="description" content><meta itemprop="image" content="/images/avatar.gif"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="AI CODE"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">Bitmap占用内存大小及加载解析</h1><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i></span> <time title="创建于" itemprop="dateCreated datePublished" datetime="2017-07-10T00:00:00+08:00">2017-07-10</time> <span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-calendar-check-o"></i></span> <time title="更新于" itemprop="dateModified" datetime="2020-12-24T10:28:48+08:00">2020-12-24</time></span> <span class="post-category"><span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-folder-o"></i></span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a></span></span> <span class="post-meta-divider">|</span><span class="page-pv"><i class="fa fa-file-o"></i> <span class="span">阅读量</span><span class="busuanzi-value" id="busuanzi_value_page_pv"></span> 次</span><div class="post-wordcount"><span class="post-meta-item-icon"><i class="fa fa-file-word-o"></i></span> <span title="字数统计">4.8k 字</span> <span class="post-meta-divider">|</span><span class="post-meta-item-icon"><i class="fa fa-clock-o"></i></span> <span title="阅读时长">20 分钟</span></div></div></header><div class="post-body" itemprop="articleBody"><p><a href="https://blog.csdn.net/smileiam/article/details/68946182" target="_blank" rel="noopener">文章转自</a></p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>在讲解图片占用内存前，我们先问自己几个问题：</p><ul><li>我们在对手机进行屏幕适时，常想可不可以只切一套图适配所有的手机呢？</li><li>一张图片加载到手机中，占用内存到底有多少？</li><li>图片占用内存跟哪些东西有关？跟手机有关系么？同一张图片放在不同的dpi文件夹下内存占用会变化么？</li><li>如果是网络图片，加载到手机中，占用内存跟手机屏幕有关系么？</li></ul><p>带着这些问题我们来一层层解析。我们先看看加载本地资源，不同手机所占内存情况：</p><h2 id="一、加载本地资源，不同手机占内存情况"><a href="#一、加载本地资源，不同手机占内存情况" class="headerlink" title="一、加载本地资源，不同手机占内存情况"></a>一、加载本地资源，不同手机占内存情况</h2><p>我们如果加载app内图片，想知道它占用多少内存，可先将此资源转成bitmap进行查看。</p><a id="more"></a><h3 id="1-从资源中获取bitmap"><a href="#1-从资源中获取bitmap" class="headerlink" title="1. 从资源中获取bitmap"></a>1. 从资源中获取bitmap</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.testxh);</span><br></pre></td></tr></table></figure><p>获取到bitmap，我们还需要知道此bitmap在内存占多少空间，具体方法如下。</p><h3 id="2-获取图片大小"><a href="#2-获取图片大小" class="headerlink" title="2. 获取图片大小"></a>2. 获取图片大小</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getBitmapSize</span><span class="params">(Bitmap bitmap)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.KITKAT)&#123;     <span class="comment">//API 19</span></span><br><span class="line">        <span class="keyword">return</span> bitmap.getAllocationByteCount();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB_MR1)&#123;<span class="comment">//API 12</span></span><br><span class="line">        <span class="keyword">return</span> bitmap.getByteCount();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bitmap.getRowBytes() * bitmap.getHeight(); <span class="comment">//earlier version</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来就来测试，不同的手机、同一张图片放在不同的密度文件夹下，占用内存情况。</p><h3 id="3-同一图片在不同屏幕的手机、不同的屏幕密度文件夹下占用内存大小"><a href="#3-同一图片在不同屏幕的手机、不同的屏幕密度文件夹下占用内存大小" class="headerlink" title="3. 同一图片在不同屏幕的手机、不同的屏幕密度文件夹下占用内存大小"></a>3. 同一图片在不同屏幕的手机、不同的屏幕密度文件夹下占用内存大小</h3><ol><li><p>经测试同一张图片分别放在不同的mipmap文件夹（mipmap-hdpi, mipmap-xhdpi, mipmap-xxhdpi）下或是drawable文件夹（drawable-hdpi, drawable-xhdpi, drawable-xxhdpi）下，相同的dpi下的文件夹下加载出来的图片，bitmap占用内存大小一样；</p></li><li><p>对于同一张图片，放在不同手机、不同的屏幕密度文件夹下占用内存情况又是如何呢，这里我们以一张大小为1024*731 = 748544B, 大小为485.11K 的图片为例，下面是测试手机占用的内存情况。</p></li></ol><p><img src="https://upload-images.jianshu.io/upload_images/3846387-c52069b0d2e0b6be.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>对于不同的手机屏幕密度的手机占用内存大小<br>从上表可以看出不同屏幕密度的手机加载图片，如果图片放在与自己屏幕密度相同的文件夹下，占用的内存都是2994176B，与图片本身大小748544B存在一个<strong>4倍关系</strong>，因为图片采用的ARGB-888色彩格式，每个像素点占用4个字节。</p><p>从上述测试可以得出，bitmap占用内存大小，与手机的屏幕密度、图片所放文件夹密度、图片的色彩格式有关。</p><p>这里总结一下获取Bitmap图片大小的代码：手机在加载图片时，会先查找自己本密度的文夹下是否存在资源，不存在则会向上查找，再向下查找，并对图片进行相应倍数的缩放：</p><ul><li><p>如果在与自己屏幕密度相同的文件夹下存在此资源，会原样显示出来，占用内存正好是: 图片的分辨率*色彩格式占用字节数；</p></li><li><p>若自己屏幕密度相同的文件夹下不存在此文件，而在大于自己屏幕密度的文件夹下存在此资源，会进行缩小相应的倍数的平方；</p></li><li><p>若在大于自己屏幕密度的文件夹下没找到此资源，则会向小于自己屏幕密度的文件夹下查找，如果存在，则会进行放大相应的倍数的平方，这两种情况图片占用内存为:</p></li></ul><p><strong>占用内存=图片宽度 X 图片高度/((资源文件夹密度/手机屏幕密度)^2) * 色彩格式每一个像素占用字节数</strong></p><h3 id="4-图片占用内存与图片的色彩格式的关系"><a href="#4-图片占用内存与图片的色彩格式的关系" class="headerlink" title="4. 图片占用内存与图片的色彩格式的关系"></a>4. 图片占用内存与图片的色彩格式的关系</h3><p>我们在计算bitmap大小时，是通过计算getRowBytes * bitmap.getHeight()得来的，后面的乘数就是图片的高度，而第一个乘数getRowBytes是什么呢？我们根进Bitmap代码查看getRowBytes函数：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 返回位图像素的行的字节数，由位图存储的像素值有关，它会根据Color类进行打包</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">getRowBytes</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mRecycled) &#123;</span><br><span class="line">        Log.w(TAG, <span class="string">"Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> nativeRowBytes(mNativePtr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>该方法最终调用的是Bitmap中的native方法：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">int</span> <span class="title">nativeRowBytes</span><span class="params">(<span class="keyword">long</span> nativeBitmap)</span></span>;</span><br></pre></td></tr></table></figure><p>我们再查看对应的Bitmap.cpp里的nativeRowBytes方法</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> jint <span class="title">Bitmap_rowBytes</span><span class="params">(JNIEnv* env, jobject, jlong bitmapHandle)</span> </span>&#123;</span><br><span class="line">     SkBitmap* bitmap = reinterpret_cast&lt;SkBitmap*&gt;(bitmapHandle)</span><br><span class="line">     <span class="keyword">return</span> static_cast&lt;jint&gt;(bitmap-&gt;rowBytes());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们可以看到这里的bitmap形式是以SkBitmap对象展现的，这个Bitmap就和图片展示的色彩格式有关,我们再看看SkBitmap里是怎么计算rowBytes的：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">size_t SkBitmap::ComputeRowBytes(Config c, <span class="keyword">int</span> width) &#123;</span><br><span class="line">    <span class="keyword">return</span> SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">SkImageInfo.h</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">SkColorTypeBytesPerPixel</span><span class="params">(SkColorType ct)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">const</span> uint8_t gSize[] = &#123;</span><br><span class="line">    <span class="number">0</span>,  <span class="comment">// Unknown</span></span><br><span class="line">    <span class="number">1</span>,  <span class="comment">// Alpha_8</span></span><br><span class="line">    <span class="number">2</span>,  <span class="comment">// RGB_565</span></span><br><span class="line">    <span class="number">2</span>,  <span class="comment">// ARGB_4444</span></span><br><span class="line">    <span class="number">4</span>,  <span class="comment">// RGBA_8888</span></span><br><span class="line">    <span class="number">4</span>,  <span class="comment">// BGRA_8888</span></span><br><span class="line">    <span class="number">1</span>,  <span class="comment">// kIndex_8</span></span><br><span class="line">  &#125;;</span><br><span class="line">  SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + <span class="number">1</span>),</span><br><span class="line">                size_mismatch_with_SkColorType_enum);</span><br><span class="line"></span><br><span class="line">   SkASSERT((size_t)ct &lt; SK_ARRAY_COUNT(gSize));</span><br><span class="line">   <span class="keyword">return</span> gSize[ct];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> inline size_t <span class="title">SkColorTypeMinRowBytes</span><span class="params">(SkColorType ct, <span class="keyword">int</span> width)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> width * SkColorTypeBytesPerPixel(ct);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，图片的宽乘以了一个SkColorTypeBytesPerPixel(ct)变量，对于不同色彩格式，每个像素占用的字节数就是在SkColorTypeBytesPerPixel中定义的。这就是为什么上面得出的bitmap大小，在自己屏幕密度的文件夹下图片占用的内存大小都被乘以了<strong>4</strong>,因为bitmap加载默认采用的是RGBA_8888编码格式。</p><h3 id="5-图片占用内存与手机屏幕密度、图片所在文件夹密度的关系"><a href="#5-图片占用内存与手机屏幕密度、图片所在文件夹密度的关系" class="headerlink" title="5. 图片占用内存与手机屏幕密度、图片所在文件夹密度的关系"></a>5. 图片占用内存与手机屏幕密度、图片所在文件夹密度的关系</h3><p>那么手机怎么加载图片时，为什么同样的图片在不同的屏幕分辨率的手机上、不同的屏幕密度文件夹下占用内存会相差这么大呢？</p><p>在加载资源图片时，我们一般会借助于BitmapFactory的decodeResource方法，此方法的源代码如下：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> res   包含图片资源的Resources对象，一般通过getResources()即可获取</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id    资源文件id, 如R.mipmap.ic_laucher</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> opts  可为空，控制采样或图片是否需要完全解码还是只需要获取图片大小</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span>      解码的bitmap</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Bitmap <span class="title">decodeResource</span><span class="params">(Resources res, <span class="keyword">int</span> id, Options opts)</span> </span>&#123;</span><br><span class="line">    Bitmap bm = <span class="keyword">null</span>;</span><br><span class="line">    InputStream is = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> TypedValue value = <span class="keyword">new</span> TypedValue();</span><br><span class="line">        <span class="comment">//1.读取资源返回数据流格式，最终会调用AssetManager的openNonAsset方法进行读取资源</span></span><br><span class="line">        is = res.openRawResource(id, value);</span><br><span class="line">        <span class="comment">//2. 根据数据流格式进行解码，在直接加载res资源时，一般opts为空</span></span><br><span class="line">        bm = decodeResourceStream(res, value, is, <span class="keyword">null</span>, opts);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (is != <span class="keyword">null</span>) is.close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="comment">// Ignore</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (bm == <span class="keyword">null</span> &amp;&amp; opts != <span class="keyword">null</span> &amp;&amp; opts.inBitmap != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Problem decoding into existing bitmap"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> bm;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们再来看看BitmapFactory的decodeResourceStream方法</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 根据输入的数据流确码成一个新的bitmap, 数据流是从资源处获取，在这里可以根据规则对图片进行一些缩放操作</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Bitmap <span class="title">decodeResourceStream</span><span class="params">(Resources res, TypedValue value,</span></span></span><br><span class="line"><span class="function"><span class="params">        InputStream is, Rect pad, Options opts)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (opts == <span class="keyword">null</span>) &#123;<span class="comment">//如果没有设置Options，系统会新创建一个Options对象</span></span><br><span class="line">        opts = <span class="keyword">new</span> Options();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//若没有设置opts，inDensity就是初始值0,它代表图片资源密度</span></span><br><span class="line">    <span class="keyword">if</span> (opts.inDensity == <span class="number">0</span> &amp;&amp; value != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> density = value.density;</span><br><span class="line">        <span class="keyword">if</span> (density == TypedValue.DENSITY_DEFAULT) &#123; <span class="comment">//如果density等于0,则采用默认值160</span></span><br><span class="line">            opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (density != TypedValue.DENSITY_NONE) &#123;<span class="comment">//如果没有设置资源密度，则图片不会被缩放</span></span><br><span class="line">            opts.inDensity = density;<span class="comment">//这里density的值对应的就是资源密度值</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//此时inTargetDensity默认也为0</span></span><br><span class="line">    <span class="keyword">if</span> (opts.inTargetDensity == <span class="number">0</span> &amp;&amp; res != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//将手机的屏幕密度值赋值给最终图片显示的密度</span></span><br><span class="line">        opts.inTargetDensity = res.getDisplayMetrics().densityDpi;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> decodeStream(is, pad, opts);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到这里调用了native decodeStream方法：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> jobject <span class="title">doDecode</span><span class="params">(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">......</span><br><span class="line">    <span class="keyword">if</span> (env-&gt;GetBooleanField(options, gOptions_scaledFieldID)) &#123;</span><br><span class="line">        <span class="comment">//资源本身的密度</span></span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">int</span> density = env-&gt;GetIntField(options, gOptions_densityFieldID);</span><br><span class="line">        <span class="comment">//最终加载的图片的密度</span></span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">int</span> targetDensity = env-&gt;GetIntField(options, gOptions_targetDensityFieldID);</span><br><span class="line">        <span class="comment">//手机的屏幕密度</span></span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">int</span> screenDensity = env-&gt;GetIntField(options, gOptions_screenDensityFieldID);</span><br><span class="line">        <span class="comment">//如果资源密度不为0，手机屏幕密度也不为0, 资源的密度与屏幕密度不相等时，图片缩放比例=屏幕密度/资源密度，如对于三星手机屏幕密度为640,如果图片放在文件夹为xhdpi 320下，则scale=2,会对图片长宽均放大2倍</span></span><br><span class="line">        <span class="keyword">if</span> (density != <span class="number">0</span> &amp;&amp; targetDensity != <span class="number">0</span> &amp;&amp; density != screenDensity) &#123;</span><br><span class="line">            scale = (<span class="keyword">float</span>) targetDensity / density;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> bool willScale = scale != <span class="number">1.0f</span>;<span class="comment">//判断是否需要缩放</span></span><br><span class="line">......</span><br><span class="line">SkBitmap decodingBitmap;</span><br><span class="line"><span class="keyword">if</span> (!decoder-&gt;decode(stream, &amp;decodingBitmap, prefColorType,decodeMode)) &#123;</span><br><span class="line">   <span class="keyword">return</span> nullObjectReturn(<span class="string">"decoder-&gt;decode returned false"</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//这里这个deodingBitmap就是解码出来的bitmap，大小是图片原始的大小</span></span><br><span class="line"><span class="keyword">int</span> scaledWidth = decodingBitmap.width();</span><br><span class="line"><span class="keyword">int</span> scaledHeight = decodingBitmap.height();</span><br><span class="line"><span class="keyword">if</span> (willScale &amp;&amp; decodeMode != SkImageDecoder::kDecodeBounds_Mode) &#123;</span><br><span class="line">    scaledWidth = <span class="keyword">int</span>(scaledWidth * scale + <span class="number">0.5f</span>);<span class="comment">//这里+0.5是保证在图片缩小时，可能会出小数，这里加0.5是为了让除后的数向上取整</span></span><br><span class="line">    scaledHeight = <span class="keyword">int</span>(scaledHeight * scale + <span class="number">0.5f</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (willScale) &#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">float</span> sx = scaledWidth / <span class="keyword">float</span>(decodingBitmap.width());</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">float</span> sy = scaledHeight / <span class="keyword">float</span>(decodingBitmap.height());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置解码图片的colorType</span></span><br><span class="line">    SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType());</span><br><span class="line">     <span class="comment">//设置图片的宽高</span></span><br><span class="line">    outputBitmap-&gt;setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,</span><br><span class="line">            colorType, decodingBitmap.alphaType()));</span><br><span class="line">    <span class="keyword">if</span> (!outputBitmap-&gt;allocPixels(outputAllocator, NULL)) &#123;</span><br><span class="line">        <span class="keyword">return</span> nullObjectReturn(<span class="string">"allocation failed for scaled bitmap"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (outputAllocator != &amp;javaAllocator) &#123;</span><br><span class="line">        outputBitmap-&gt;eraseColor(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    SkPaint paint;</span><br><span class="line">    paint.setFilterLevel(SkPaint::kLow_FilterLevel);</span><br><span class="line"></span><br><span class="line">    <span class="function">SkCanvas <span class="title">canvas</span><span class="params">(*outputBitmap)</span></span>;</span><br><span class="line">    canvas.scale(sx, sy);<span class="comment">//根据缩放比画出图像</span></span><br><span class="line">    canvas.drawBitmap(decodingBitmap, <span class="number">0.0f</span>, <span class="number">0.0f</span>, &amp;paint);<span class="comment">//将图片画到画布上</span></span><br><span class="line">&#125;</span><br><span class="line">......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>inDensity，inTargetDensity，inScreenDensity, inScaled三者关系</strong></p><p>通过追查代码，我们可以看到图片资源通过数据流解码时，会根据inDensity，inTargetDensity，inScreenDensity三个值和是否被缩放标识inScaled</p><ul><li>inDensity：图片本身的像素密度（其实就是图片资源所在的哪个密度文件夹下，如在xxhdpi下就是480，如果在asstes、手机内存／sd卡下，默认是160）；</li><li>inTargetDensity：图片最终在bitmap里的像素密度，如果没有赋值，会将inTargetDensity设置成inScreenDensity；</li><li>inScreenDensity：手机本身的屏幕密度，如我们测试的三星手机dpi=640, 如果inDensity与inTargetDensity不相等时，就需要对图片进行缩放，inScaled = inTargetDensity／inDensity。</li></ul><p>我们上面研究了加载应用程序的图片占用内存大小与手机屏幕密码和图片所放的密度文件夹、图片的编码格式有关，那如果加载的是网络图片或是本地图片，在不同的手机上占用内存又是否一样呢？</p><h2 id="二、加载sd卡下的资源或是网络图片解析"><a href="#二、加载sd卡下的资源或是网络图片解析" class="headerlink" title="二、加载sd卡下的资源或是网络图片解析"></a>二、加载sd卡下的资源或是网络图片解析</h2><p>手机无论是加载sd卡图片，assets路径下还是网络图片，都需要先把图片读成数据流格式，再调用相应的decodeStream方法，将数据流转成bitmap形式，在调用decodeStream如果不设置Options的话，通过以上三款手机打印出图片所占内存大小均为：2994176B，也就是跟手机的屏幕密度没有关系。</p><p>那如果设置Options中的参数，图片占用的内存会不会与手机的屏幕密度有关系呢？我在测试中发现单独手动设置图片密度inDensity或是inTargetDensity，并不起作用，图片占用内存一直都是图片本身大小。</p><p>为什么没起作用呢，这需要我们从资源加载的源头看起。</p><h3 id="1-根据手机本地图片路径获取Bitmap"><a href="#1-根据手机本地图片路径获取Bitmap" class="headerlink" title="1. 根据手机本地图片路径获取Bitmap"></a>1. 根据手机本地图片路径获取Bitmap</h3><p>我们先来看一下BitmapFactory的decodeFile函数：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//读取手机本地的图片资源</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Bitmap <span class="title">decodeFile</span><span class="params">(String pathName, Options opts)</span> </span>&#123;</span><br><span class="line">        Bitmap bm = <span class="keyword">null</span>;</span><br><span class="line">        InputStream stream = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            stream = <span class="keyword">new</span> FileInputStream(pathName);</span><br><span class="line">            <span class="comment">//调用decodeStream将数据流转成bitmap</span></span><br><span class="line">            bm = decodeStream(stream, <span class="keyword">null</span>, opts);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="comment">/*  do nothing.</span></span><br><span class="line"><span class="comment">                If the exception happened on open, bm will be null.</span></span><br><span class="line"><span class="comment">            */</span></span><br><span class="line">            Log.e(<span class="string">"BitmapFactory"</span>, <span class="string">"Unable to decode stream: "</span> + e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (stream != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    stream.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                    <span class="comment">// do nothing here</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> bm;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="2-根据网络地址获取图片Bitmap"><a href="#2-根据网络地址获取图片Bitmap" class="headerlink" title="2. 根据网络地址获取图片Bitmap"></a>2. 根据网络地址获取图片Bitmap</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 从网络中获取图片，先获取数据流，再转成Bitmap</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Bitmap <span class="title">getBitmapByPicUrl</span><span class="params">(String picurl)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    InputStream inputStream = <span class="keyword">null</span>;</span><br><span class="line">    URL url = <span class="keyword">new</span> URL(picurl);                    <span class="comment">//服务器地址</span></span><br><span class="line">    <span class="keyword">if</span> (url != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//打开连接</span></span><br><span class="line">        HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();</span><br><span class="line">        httpURLConnection.setConnectTimeout(<span class="number">3000</span>);<span class="comment">//设置网络连接超时的时间为3秒</span></span><br><span class="line">        httpURLConnection.setRequestMethod(<span class="string">"GET"</span>);        <span class="comment">//设置请求方法为GET</span></span><br><span class="line">        httpURLConnection.setDoInput(<span class="keyword">true</span>);                <span class="comment">//打开输入流</span></span><br><span class="line">        <span class="keyword">int</span> responseCode = httpURLConnection.getResponseCode();    <span class="comment">// 获取服务器响应值</span></span><br><span class="line">        <span class="keyword">if</span> (responseCode == HttpURLConnection.HTTP_OK) &#123;        <span class="comment">//正常连接</span></span><br><span class="line">            inputStream = httpURLConnection.getInputStream();        <span class="comment">//获取输入流</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> BitmapFactory.decodeStream(inputStream);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到通过路径加载图片，最终还是会调用BitmapFactory里的decodeStream方法，我们再来看看decodeStream方法。</p><h3 id="3-将数据流转成Bitmap"><a href="#3-将数据流转成Bitmap" class="headerlink" title="3. 将数据流转成Bitmap"></a>3. 将数据流转成Bitmap</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *根据输入的数据流确码成一个新的bitmap</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> is           从源数据获取的输入数居流，用于解码成bitmap</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> outPadding   如果不为空，返回bitmap的边距，这个会加入到图片所占内存大小里</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> opts         可以为空; 用来控制图片的采样率和图片是否需要完全解码，还是只需要获取图片大小</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span>             解码后的图片</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Bitmap <span class="title">decodeStream</span><span class="params">(InputStream is, Rect outPadding, Options opts)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (is == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Bitmap bm = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, <span class="string">"decodeBitmap"</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;<span class="comment">//如果数据流来自资源，则直接调用native方法</span></span><br><span class="line">        <span class="keyword">if</span> (is <span class="keyword">instanceof</span> AssetManager.AssetInputStream) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="keyword">long</span> asset = ((AssetManager.AssetInputStream) is).getNativeAsset();</span><br><span class="line">            bm = nativeDecodeAsset(asset, outPadding, opts);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            bm = decodeStreamInternal(is, outPadding, opts);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (bm == <span class="keyword">null</span> &amp;&amp; opts != <span class="keyword">null</span> &amp;&amp; opts.inBitmap != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Problem decoding into existing bitmap"</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        setDensityFromOptions(bm, opts);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> bm;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果数据流来自于资源，则调用BitmapFactory的nativeDecodeAsset，</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> Bitmap <span class="title">nativeDecodeAsset</span><span class="params">(<span class="keyword">long</span> nativeAsset, Rect padding, Options opts)</span></span>;</span><br></pre></td></tr></table></figure><p>否则调用decodeStreamInternal方法：</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * is不得为空，会根据流的需要提供一个缓冲区</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Bitmap <span class="title">decodeStreamInternal</span><span class="params">(InputStream is, Rect outPadding, Options opts)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// ASSERT(is != null);</span></span><br><span class="line">    <span class="keyword">byte</span> [] tempStorage = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (opts != <span class="keyword">null</span>) tempStorage = opts.inTempStorage;</span><br><span class="line">    <span class="comment">//如果Options没有提供inTempStorage参数会默认提供一个16M的缓冲区</span></span><br><span class="line">    <span class="keyword">if</span> (tempStorage == <span class="keyword">null</span>) tempStorage = <span class="keyword">new</span> <span class="keyword">byte</span>[DECODE_BUFFER_SIZE];</span><br><span class="line">    <span class="keyword">return</span> nativeDecodeStream(is, tempStorage, outPadding, opts);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此方法会调用native的nativeDecodeStream方法：</p><h3 id="4-native层的数据流解析"><a href="#4-native层的数据流解析" class="headerlink" title="4. native层的数据流解析"></a>4. native层的数据流解析</h3><ol><li>nativeDecodeStream／nativeDecodeAsset<br>通过追踪上述两种nativeDecodeStream方法和nativeDecodeAsset方法，它们最终都会调用nativeDecodeStreamScaled或是nativeDecodeAssetScaled方法，它们会添加两个参数，一个是false,一个是1.0f，这两个参数具体代表什么呢？</li></ol><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//解码Asset资源的数据流</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> jobject <span class="title">nativeDecodeAsset</span><span class="params">(JNIEnv* env, jobject clazz, jint native_asset,</span></span></span><br><span class="line"><span class="function"><span class="params">        jobject padding, jobject options)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> nativeDecodeAssetScaled(env, clazz, native_asset, padding, options, <span class="keyword">false</span>, <span class="number">1.0f</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//解码纯数据流</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> jobject <span class="title">nativeDecodeStream</span><span class="params">(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,</span></span></span><br><span class="line"><span class="function"><span class="params">        jobject padding, jobject options)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> nativeDecodeStreamScaled(env, clazz, is, storage, padding, options, <span class="keyword">false</span>, <span class="number">1.0f</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>nativeDecodeStreamScaled／nativeDecodeAssetScaled<br><br>nativeDecodeAssetScaled或是nativeDecodeStreamScaled方法中最后两个参数，分别是applyScale，sclae，一个是是否申请缩放，一个是缩放比例，也就是从这种数据流加载的图片，默认都不会进缩放。我们注意到，这两个函数最终都会走到doDecode方法里，我们直接看nativeDecodeStreamScaled方法，发现此方法只是对输入流进行了转换，转成SkStream类型。</li></ol><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> jobject <span class="title">nativeDecodeStreamScaled</span><span class="params">(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,</span></span></span><br><span class="line"><span class="function"><span class="params">        jobject padding, jobject options, jboolean applyScale, jfloat scale)</span> </span>&#123;</span><br><span class="line">    jobject bitmap = NULL;</span><br><span class="line">    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (stream) &#123;</span><br><span class="line">        <span class="comment">// for now we don't allow purgeable with java inputstreams</span></span><br><span class="line">        bitmap = doDecode(env, stream, padding, options, <span class="keyword">false</span>, <span class="keyword">false</span>, applyScale, scale);</span><br><span class="line">        stream-&gt;unref();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> bitmap;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>doDecode<br>我们来看最终的doDecode函数：</li></ol><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> jobject <span class="title">doDecode</span><span class="params">(JNIEnv* env, SkStream* stream, jobject padding,</span></span></span><br><span class="line"><span class="function"><span class="params">        jobject options, bool allowPurgeable, bool forcePurgeable = <span class="keyword">false</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">        bool applyScale = <span class="keyword">false</span>, <span class="keyword">float</span> scale = <span class="number">1.0</span>f)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> sampleSize = <span class="number">1</span>;</span><br><span class="line">    SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;</span><br><span class="line">    SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;<span class="comment">//直接采用ARGB_8888的色彩格式</span></span><br><span class="line">    bool doDither = <span class="keyword">true</span>;</span><br><span class="line">    bool isMutable = <span class="keyword">false</span>;</span><br><span class="line">    bool willScale = applyScale &amp;&amp; scale != <span class="number">1.0f</span>;<span class="comment">//上面传的参数applyScale为false，所以willScale为false</span></span><br><span class="line">    bool isPurgeable = !willScale &amp;&amp;</span><br><span class="line">            (forcePurgeable || (allowPurgeable &amp;&amp; optionsPurgeable(env, options)));</span><br><span class="line">    bool preferQualityOverSpeed = <span class="keyword">false</span>;</span><br><span class="line">    jobject javaBitmap = NULL;</span><br><span class="line">    <span class="keyword">if</span> (options != NULL) &#123;</span><br><span class="line">        sampleSize = env-&gt;GetIntField(options, gOptions_sampleSizeFieldID);<span class="comment">//获取采样率</span></span><br><span class="line">        <span class="keyword">if</span> (optionsJustBounds(env, options)) &#123;<span class="comment">//是否只加载图片边界，而不解码</span></span><br><span class="line">            mode = SkImageDecoder::kDecodeBounds_Mode;</span><br><span class="line">        &#125;</span><br><span class="line">       <span class="comment">//省略初始化代码</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//省略一堆代码</span></span><br><span class="line">    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);</span><br><span class="line">    <span class="keyword">int</span> scaledWidth = decoded-&gt;width();<span class="comment">//获取解码图片的宽度</span></span><br><span class="line">    <span class="keyword">int</span> scaledHeight = decoded-&gt;height();<span class="comment">//获取解码后图片的调节度</span></span><br><span class="line">    <span class="keyword">if</span> (willScale &amp;&amp; mode != SkImageDecoder::kDecodeBounds_Mode) &#123;<span class="comment">//由于willScale为false，这里不会运行</span></span><br><span class="line">        scaledWidth = <span class="keyword">int</span>(scaledWidth * scale + <span class="number">0.5f</span>);</span><br><span class="line">        scaledHeight = <span class="keyword">int</span>(scaledHeight * scale + <span class="number">0.5f</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// update options (if any)</span></span><br><span class="line">    <span class="keyword">if</span> (options != NULL) &#123;</span><br><span class="line">        env-&gt;SetIntField(options, gOptions_widthFieldID, scaledWidth);</span><br><span class="line">        env-&gt;SetIntField(options, gOptions_heightFieldID, scaledHeight);</span><br><span class="line">        env-&gt;SetObjectField(options, gOptions_mimeFieldID,</span><br><span class="line">                getMimeTypeString(env, decoder-&gt;getFormat()));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (mode == SkImageDecoder::kDecodeBounds_Mode) &#123;<span class="comment">//如果只获取图片大小，这里不会返回bitmap</span></span><br><span class="line">        <span class="keyword">return</span> NULL;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (padding) &#123;<span class="comment">//如果设置了padding，则会把边矩算进去</span></span><br><span class="line">        <span class="keyword">if</span> (peeker.fPatchIsValid) &#123;</span><br><span class="line">            GraphicsJNI::set_jrect(env, padding,</span><br><span class="line">                    peeker.fPatch-&gt;paddingLeft, peeker.fPatch-&gt;paddingTop,</span><br><span class="line">                    peeker.fPatch-&gt;paddingRight, peeker.fPatch-&gt;paddingBottom);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            GraphicsJNI::set_jrect(env, padding, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>, -<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    SkPixelRef* pr;</span><br><span class="line">    <span class="keyword">if</span> (isPurgeable) &#123;</span><br><span class="line">        pr = installPixelRef(bitmap, stream, sampleSize, doDither);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        pr = bitmap-&gt;pixelRef();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),</span><br><span class="line">            isMutable, ninePatchChunk);<span class="comment">//创建bitmap</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过上面分析直至native中的decode函数，我们发现options里的参数只提取了sampleSize、optionsJustBounds，但是没有见到inDensity，inTargetDensity，inScreenDensity等参数的提取。如果我在加载流前，设置ops.inDensity和ops.inTargetDensity参数如下，图片占用内存大小会缩小到原来的1/4</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">BitmapFactory.Options ops = <span class="keyword">new</span> BitmapFactory.Options();</span><br><span class="line">            <span class="keyword">int</span> targetDensity = getResources().getDisplayMetrics().densityDpi;</span><br><span class="line">            ops.inDensity = <span class="number">240</span>;</span><br><span class="line">            ops.inTargetDensity = <span class="number">480</span>;</span><br><span class="line">            Bitmap assetsbmp = BitmapFactory.decodeStream(stream, <span class="keyword">null</span>, ops);</span><br></pre></td></tr></table></figure><p>但是如果只设置inDensity或是inTargetDensity参数，是完全不起作用，感觉是因为只设置了一个参数，另一个参数默认为0, 前面咱们判断过，只要有一个参数为0, 就不会计算缩放比。所以默认还是显示原来图片尺寸大小，只有两个参数均设置，都不为0, 才会去计算缩放比。</p><p>通过上面的分析，我们可以回答最开始的问题了。</p><h2 id="结论："><a href="#结论：" class="headerlink" title="结论："></a>结论：</h2><p><strong>1.</strong> 在对手机进行屏幕适时，可以只切一套图适配所有的手机。</p><ul><li><p>但是如果只切一套小图，那在高屏幕密度手机上，会对图片进行放大，这样图片占用的内存往往比切相应图片放在高密度文件夹下，占用的内存还要大。</p></li><li><p>那如果只切一套大图放在高幕文件夹下，在小屏幕密度手机上，会缩小显示，按道理是行得通的。但系统在对图片进行缩放时，会进行大量计算，会对手机的性能有一定的影响。同时如果图片缩放比较狠，可能导致图片出现抖动或是毛边。</p></li><li><p>所以最好切出不同比便的图片放在不同幕度的文件夹下，对于性能要求不大高的图片，可以只切一套大图；</p></li></ul><p><strong>2.</strong> 一张图片<strong>占用内存=图片长 * 图片宽 ／ （资源图片文件密度/手机屏幕密度）^2 * 每一象素占用字节数</strong>，所以图片占用内存跟图片本身大小、手机屏幕密度、图片所在的文件夹密度，图片编码的色彩格式有关；</p><p><strong>3.</strong> 对于网络图片，在不同屏幕密度的手机上加载出来，占用内存是一样的。</p><p><strong>4.</strong> 对于网络或是assets/手机本地图片加载，如果想通过设置Options里的<br>inDensity或是inTargetDensity参数来调整图片的缩放比，必须两个参数均设置才能起作用，只设置一个，不会起作用。</p><p><strong>5.</strong> drawable和mipmap文件夹存放图片的区别，首先图片放在drawable-xhdpi和mipmap-xhdpi下，两者占用的内存是一样的，<br>Mipmaps早在Android2.2+就可以用了，但是直到4.3 google才强烈建议使用。把图片放到mipmaps可以提高系统渲染图片的速度，提高图片质量，减少GPU压力。其他并没有什么区别。</p></div><div><ul class="post-copyright"><li class="post-copyright-author"> <strong>本文作者：</strong></li><li class="post-copyright-link"> <strong>本文链接：</strong> <a href="https://aicode.vip/2017/07/10/Bitmap占用内存大小及加载解析/" title="Bitmap占用内存大小及加载解析">https://aicode.vip/2017/07/10/Bitmap占用内存大小及加载解析/</a></li><li class="post-copyright-license"> <strong>版权声明：</strong> 本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/" rel="external nofollow" target="_blank">CC BY-NC-SA 3.0</a> 许可协议。转载请注明出处！</li></ul></div><footer class="post-footer"><div class="post-tags"> <a href="/tags/Bitmap/" rel="tag"># Bitmap</a></div><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/2017/03/10/Android-Studio-NDK-Cmake/" rel="next" title="Android-Studio-NDK-Cmake"><i class="fa fa-chevron-left"></i> Android-Studio-NDK-Cmake</a></div><span class="post-nav-divider"></span><div class="post-nav-prev post-nav-item"> <a href="/2017/07/11/Bitmap的内存，加载和回收/" rel="prev" title="Bitmap的内存，加载和回收">Bitmap的内存，加载和回收<i class="fa fa-chevron-right"></i></a></div></div></footer></div></article><div class="post-spread"></div></div></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span><span class="sidebar-toggle-line sidebar-toggle-line-middle"></span><span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> 文章目录</li><li class="sidebar-nav-overview" data-target="site-overview-wrap"> 站点概览</li></ul><section class="site-overview-wrap sidebar-panel"><div class="site-overview"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"><p class="site-author-name" itemprop="name"></p><p class="site-description motion-element" itemprop="description">世上无难事<br>只要肯登攀<br></p></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"> <a href="/archives/"><span class="site-state-item-count">21</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories/index.html"><span class="site-state-item-count">6</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags/index.html"><span class="site-state-item-count">16</span> <span class="site-state-item-name">标签</span></a></div></nav><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/lqxue" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i> GitHub</a></span><span class="links-of-author-item"><a href="https://www.jianshu.com/u/d21787b52bdc" target="_blank" title="简书"><i class="fa fa-fw fa-book"></i> 简书</a></span></div></div></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#问题"><span class="nav-text">问题</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#一、加载本地资源，不同手机占内存情况"><span class="nav-text">一、加载本地资源，不同手机占内存情况</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-从资源中获取bitmap"><span class="nav-text">1. 从资源中获取bitmap</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-获取图片大小"><span class="nav-text">2. 获取图片大小</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-同一图片在不同屏幕的手机、不同的屏幕密度文件夹下占用内存大小"><span class="nav-text">3. 同一图片在不同屏幕的手机、不同的屏幕密度文件夹下占用内存大小</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-图片占用内存与图片的色彩格式的关系"><span class="nav-text">4. 图片占用内存与图片的色彩格式的关系</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-图片占用内存与手机屏幕密度、图片所在文件夹密度的关系"><span class="nav-text">5. 图片占用内存与手机屏幕密度、图片所在文件夹密度的关系</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#二、加载sd卡下的资源或是网络图片解析"><span class="nav-text">二、加载sd卡下的资源或是网络图片解析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-根据手机本地图片路径获取Bitmap"><span class="nav-text">1. 根据手机本地图片路径获取Bitmap</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-根据网络地址获取图片Bitmap"><span class="nav-text">2. 根据网络地址获取图片Bitmap</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-将数据流转成Bitmap"><span class="nav-text">3. 将数据流转成Bitmap</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-native层的数据流解析"><span class="nav-text">4. native层的数据流解析</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#结论："><span class="nav-text">结论：</span></a></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright">&copy; <span itemprop="copyrightYear">2020</span><span class="with-love"><i class="fa fa-user"></i></span><span class="author" itemprop="copyrightHolder"></span></div><div class="busuanzi-count"><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="site-uv"><i class="fa fa-user"></i> <span class="span">访客</span><span class="busuanzi-value" id="busuanzi_value_site_uv"></span> 人次</span><span class="site-pv"><i class="fa fa-eye"></i> <span class="span">访问量</span><span class="busuanzi-value" id="busuanzi_value_site_pv"></span> 次</span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i> <span id="scrollpercent"><span>0</span>%</span></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script><script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script></body></html>